import torch
from torch import nn
from d2l import torch as d2l

# def pool2d(X ,pool_size,mode='max'):
#     p_h , p_w = pool_size
#     Y = torch.zeros((X.shape[0]-p_h+1,X.shape[1]-p_w+1))
#     for i in range(Y.shape[0]):
#         for j in range(Y.shape[1]):
#             if mode == 'max':
#                 Y[i][j] = X[i:i+p_h,j:j+p_w].max()
#             elif mode == 'avg':
#                 Y[i][j] = X[i:i+p_h,j:j+p_w].mean()
#     return Y


# LeNet
# class Reshape(torch.nn.Module):
#     def forward(self,x):
#         return x.view(-1,1,28,28)
#
# net = nn.Sequential(
#     Reshape(),nn.Conv2d(1,6,kernel_size=5,padding=2),nn.Sigmoid(),
#     nn.AvgPool2d(kernel_size=2,stride=2),
#     nn.Conv2d(6,16,kernel_size=5),nn.Sigmoid(),
#     nn.AvgPool2d(kernel_size=2,stride=2),nn.Flatten(),
#     nn.Linear(16*5*5,120),nn.Sigmoid(),
#     nn.Linear(120,84),nn.Sigmoid(),
#     nn.Linear(84,10))
#
# # X = torch.rand(size=(1,1,28,28),dtype=torch.float32)
# # for layer in net:
# #     X = layer(X)
# #     print(f"{layer.__class__.__name__}'output shape\t'{X.shape}")
#
# batch_size = 256
# train_iter , test_iter = d2l.load_data_fashion_mnist(batch_size)

# AlexNet
#
# net = nn.Sequential(
#     nn.Conv2d(1,96,kernel_size=11,stride=4,padding=1),nn.ReLU(),
#     nn.MaxPool2d(kernel_size=3,stride=2),
#     nn.Conv2d(96,256,kernel_size=5,padding=2),nn.ReLU(),
#     nn.MaxPool2d(kernel_size=3,stride=2),
#     nn.Conv2d(256,384,kernel_size=3,padding=1),nn.ReLU(),
#     nn.Conv2d(384,384,kernel_size=3,padding=1),nn.ReLU(),
#     nn.Conv2d(384,256,kernel_size=3,padding=1),nn.ReLU(),
#     nn.MaxPool2d(kernel_size=3,stride=2),nn.Flatten(),
#     nn.Linear(6400,4096),nn.Dropout(p=0.5),
#     nn.Linear(4096,4096),nn.Dropout(p=0.5),
#     nn.Linear(4096,10))
#
# X = torch.randn(1,1,224,224)
# for layer in net:
#     X = layer(X)
#     print(f"{layer.__class__.__name__},'outputshape:\t{X.shape}")
#

def vgg_block(num_convs,in_channels,out_channels):
    layers = []
    for _ in range(num_convs):
        layers.append(nn.Conv2d(in_channels,out_channels,kernel_size=3,padding=1))
        layers.append(nn.ReLU())
        in_channels = out_channels
    layers.append(nn.MaxPool2d(kernel_size=2,stride=1))
    return nn.Sequential(*layers)

conv_arch = ((1,64),(1,128),(2,256),(2,512),(2,512))

def vgg(conv_arch):
    conv_blks = []
    in_channels = 1
    for(num_convs,out_channels) in conv_arch:
        conv_blks.append(vgg_block(num_convs,in_channels,out_channels))
        in_channels = out_channels

    return nn.Sequential(
        *conv_blks,nn.Flatten(),
        nn.Linear(out_channels*7*7,4096),nn.ReLU(),
        nn.Dropout(0.5),nn.Linear(4096,4096),nn.ReLU(),
        nn.Dropout(0.5),nn.Linear(4096,10))

net = vgg(conv_arch)